﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

//读取状态
public class ReadState
{
    //客户端ID
    public string SessionId { get; set; }
    //包头长度
    public int HeadLength = 8;
    //协议ID
    public Int32 MsgId = 0;
    //包体长度
    public Int32 BodyLength = 0;
    //客户端socket
    public Socket WorkSocket = null;
    //消息头
    public byte[] HeadBytes
    {
        get
        {
            return m_HeadBytes;
        }
        set
        {
            m_HeadBytes = value;
        }
    }
    //解析消息头
    public void ParserHead()
    {
        m_buffer = new ByteBuffer(m_HeadBytes);
        MsgId = m_buffer.ReadInt32();
        BodyLength = m_buffer.ReadInt32();
    }
    //消息体
    public byte[] BodyBytes { get; set; }
    //消息头
    private byte[] m_HeadBytes = null;
    //解析类
    private ByteBuffer m_buffer = null;
}

//发送状态
public class SendState
{
    public Socket RemoteSocket { get; set; }
    public byte[] MsgBits { get; set; }
    public CustomClient Client { get; set; }
    public bool CloseClient { get; set; }
}

//TCP异步服务器实例
public class TCPAsyncServer
{
    public event TCPServer.MainNotifyHandler MainNotify;
    public event TCPServer.ReadErrorHandler ReadSocketError;
    public event TCPServer.SendErrorHandler SendSocketError;
    public event TCPServer.SendFinishHandler SendFinish;
    /// <summary>
    /// 获取和设置Header的长度
    /// </summary>
    public int HeaderLength { get; set; }
    public string Name { get; set; }

    public static ManualResetEvent allDone = new ManualResetEvent(false);

    public bool PrintReceiveHex = false;
    public bool PrintSendHex = false;
    public bool Started = true;
    private Socket m_socket;

    public void Start(int port)
    {
        IPEndPoint ipe = new IPEndPoint(IPAddress.Any, port);
        m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //绑定端口和IP  
        m_socket.Bind(ipe);
        //设置监听  
        m_socket.Listen(10);

        //开启多线程发送心跳包
        m_checkClientThread = new Thread(new ThreadStart(ScanOffline));
        m_checkClientThread.IsBackground = true;
        m_checkClientThread.Start();

        LOG.InfoFormat("({1}) 服务器开启 端口:{0}", port,Name);
        LOG.InfoFormat("开始监听客户端连接!");
        while (Started)
        {
            allDone.Reset();
            m_socket.BeginAccept(
                new AsyncCallback(AcceptCallback),
                m_socket);
            allDone.WaitOne();
            break;
        }

       
    }

    public void Stop()
    {
        Started = false;
        m_socket.Close();
    }

    private void ScanOffline()
    {
        while (true)
        {
            System.Threading.Thread.Sleep(1000);

            lock (PlayerMgr.singleton.PlayerDic)
            {
                Dictionary<string,Player>.Enumerator e = PlayerMgr.singleton.PlayerDic.GetEnumerator();

                while (e.MoveNext())
                {
                    if()

                }


                foreach (Int32 clientID in _DicClient.Keys)
                {
                    ClientInfo clientInfo = _DicClient[clientID];

                    // 如果已经离线则不用管
                    if (!clientInfo.State)
                    {
                        continue;
                    }

                    // 判断最后心跳时间是否大于3秒
                    TimeSpan sp = System.DateTime.Now - clientInfo.LastHeartbeatTime;
                    if (sp.Seconds >= 3)
                    {
                        // 离线，触发离线事件
                        if (OnClientOffline != null)
                        {
                            OnClientOffline(clientInfo);
                        }

                        // 修改状态
                        clientInfo.State = false;
                    }
                }
            }
        }

    }

    public void AcceptCallback(IAsyncResult ar)
    {
        // Create the state object.
        ReadState readState = new ReadState();
        try
        {
            allDone.Set();

            Socket listener = (Socket) ar.AsyncState;
            Socket handler = listener.EndAccept(ar);

            string sessionId = TCPClients.AssignSessionId();
            LOG.InfoFormat("({2}) 新客户端连接:{0},客户端id:{1}", handler.RemoteEndPoint, sessionId,Name);

            CustomClient client = new CustomClient();
            client.socket = handler;
            client.SessionId = sessionId;
            TCPClients.Add(client.SessionId, client);
            LOG.InfoFormat("({1}) 当前客户端数量:{0}",TCPClients.ClientCount,Name);

            readState.WorkSocket = handler;
            readState.SessionId = client.SessionId;

            readState.HeadBytes = new byte[readState.HeadLength];
            handler.BeginReceive(readState.HeadBytes, 0, readState.HeadLength, SocketFlags.None, new AsyncCallback(ReadHeadCallback), readState);
        }
        catch (Exception e)
        {
            ReadException(e,readState);
        }
    }

    public void ReadHeadCallback(IAsyncResult ar)
    {
        ReadState readState = (ReadState)ar.AsyncState;
        try
        {
            Socket handler = readState.WorkSocket;
            if (!handler.Connected)
            {
                LOG.InfoFormat("({0}) Connection closed!",Name);
                return;
            }
            LOG.Log(string.Format("收到 客户端:{0} 的新消息!", readState.SessionId));

            //接收8字节包头 四个字节包体长，四个字节协议号
            int bytesRead = handler.EndReceive(ar);

            if (bytesRead > 0)
            {
                readState.ParserHead();

                LOG.InfoFormat("({2}) Read header from:{0},body len:{1}", handler.RemoteEndPoint, readState.BodyLength, Name);
                if (PrintReceiveHex)
                {
                    PrintUtils.PrintHex(readState.HeadBytes);
                }
                if (readState.BodyLength > 0)
                {
                    //开始读取包体
                    readState.BodyBytes = new byte[readState.BodyLength];
                    handler.BeginReceive(readState.BodyBytes, 0, readState.BodyLength, SocketFlags.None, new AsyncCallback(ReadBodyCallback), readState);
                }
                else
                {
                    //MainNotify?.Invoke(readState, null);
                    ResetState(readState);
                }
            }
            else
            {
                //客户端断开连接
                LOG.Log("断开连接!");
            }
        }
        catch (Exception e)
        {
            ReadException(e,readState);
        }
    }

    /// <summary>
    /// 读到包体
    /// </summary>
    /// <param name="ar"></param>
    public void ReadBodyCallback(IAsyncResult ar)
    {
        ReadState readState = (ReadState)ar.AsyncState;
        try
        {
            Socket handler = readState.WorkSocket;

            int bytesRead = handler.EndReceive(ar);
            if (bytesRead > 0)
            {
                if (PrintReceiveHex)
                {
                    PrintUtils.PrintHex(readState.BodyBytes);
                }
                //分发协议
                ProtoPrecess.singleton.Process(readState.SessionId,readState.MsgId,readState.BodyLength,readState.BodyBytes);
                //重置开始读取包头
                ResetState(readState);
            }
        }
        catch (Exception e)
        {
            ReadException(e,readState);
        }
    }

    //分发协议
   


    private void ResetState(ReadState readState)
    {
        Socket handler = readState.WorkSocket;
        //将客户端状态重置为接收包头状态
        readState.HeadBytes = new byte[readState.HeadLength];
        handler.BeginReceive(readState.HeadBytes, 0, readState.HeadLength, SocketFlags.None, new AsyncCallback(ReadHeadCallback), readState);
    }

    public void Send(string sessionId,string msg,bool closeClient)
    {
        if (string.IsNullOrEmpty(sessionId))
        {
            throw new Exception("Session id is null!");
        }
        CustomClient client = TCPClients.GetClient(sessionId);
        if (client == null)
        {
            throw new Exception("Not found client by session:" + sessionId);
        }
        LOG.DebugFormat("({2}) Send {0} bytes to {1}", msg.Length, client.socket.RemoteEndPoint, Name);
        if (PrintSendHex)
        {
            PrintUtils.PrintHex(msg);
        }
        SendState state = new SendState();
        state.CloseClient = closeClient;
        state.RemoteSocket = client.socket;
        state.Client = client;
        state.MsgBits = Encoding.ASCII.GetBytes(msg);
        // Begin sending the data to the remote device.
        client.socket.BeginSend(state.MsgBits, 0, state.MsgBits.Length, 0,
            new AsyncCallback(SendCallback), state);
    }

    public void Send(string sessionId, byte[] bytes,bool closeClient)
    {

        try
        {
            if (string.IsNullOrEmpty(sessionId))
            {
                throw new Exception("Session id is null!");
            }
            CustomClient client = TCPClients.GetClient(sessionId);
            if (client == null)
            {
                throw new Exception("Not found client by session:"+ sessionId);
            }
            LOG.DebugFormat("({2}) Send {0} bytes to {1}", bytes.Length, client.socket.RemoteEndPoint,Name);
            if (PrintSendHex)
            {
                PrintUtils.PrintHex(bytes);
            }
            SendState state = new SendState();
            state.CloseClient = closeClient;
            state.RemoteSocket = client.socket;
            state.Client = client;
            client.socket.BeginSend(bytes, 0, bytes.Length, 0,
                new AsyncCallback(SendCallback), state);
        }
        catch (Exception e)
        {
            ReadException(e,null);
        }
    }


    private void SendCallback(IAsyncResult ar)
    {
        SendState handler = (SendState)ar.AsyncState;
        try
        {
            Socket socket = handler.RemoteSocket;
            int bytesSent = socket.EndSend(ar);
            LOG.DebugFormat("({2}) Sent {0} bytes to {1} finish!", bytesSent, socket.RemoteEndPoint,Name);
            SendFinish?.Invoke(handler);
            if (handler.CloseClient)
            {
                TCPClients.Close(handler.Client.SessionId);
                LOG.InfoFormat("({1}) Client :{0} closed!", handler.Client.SessionId,Name);
            }
        }
        catch (Exception e)
        {
            WriteException(e, handler);
        }
    }

    private void ReadException(Exception e, ReadState state)
    {
        LOG.Error("xxServer read error",e);
        ReadSocketError?.Invoke(e,state);
    }

    private void WriteException(Exception e, SendState state)
    {
        LOG.Error("xxServer write error", e);
        SendSocketError?.Invoke(e, state);
    }

    private Thread m_checkClientThread = null;

}
